﻿using System;
using System.Diagnostics.Contracts;

namespace BlindData
{
	public static class BetweenGenerator
	{
		/// <summary>
		/// Returns a random int within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random int returned.</param>
		/// <param name="maxValue">The upper bound of the random int returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static int Between(this INext<int> next, int minValue, int maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			return (int)(((double)maxValue - (double)minValue) * next.Generator.NextProbability() + (double)minValue);
		}

		/// <summary>
		/// Returns a random long within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random long returned.</param>
		/// <param name="maxValue">The upper bound of the random long returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static long Between(this INext<long> next, long minValue, long maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			return (long)(((double)maxValue - (double)minValue) * next.Generator.NextProbability() + (double)minValue);
		}

		/// <summary>
		/// Returns a random double within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random double returned.</param>
		/// <param name="maxValue">The upper bound of the random double returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static double Between(this INext<double> next, double minValue, double maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			return (maxValue - minValue) * next.Generator.NextProbability() + minValue;
		}

		/// <summary>
		/// Returns a random float within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random float returned.</param>
		/// <param name="maxValue">The upper bound of the random float returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static float Between(this INext<float> next, float minValue, float maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			return (float)(((double)maxValue - (double)minValue) * next.Generator.NextProbability() + (double)minValue);
		}

		/// <summary>
		/// Returns a random decimal within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random decimal returned.</param>
		/// <param name="maxValue">The upper bound of the random decimal returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static decimal Between(this INext<decimal> next, decimal minValue, decimal maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			return (decimal)(((double)maxValue - (double)minValue) * next.Generator.NextProbability() + (double)minValue);
		}

		#region DateTime

		/// <summary>
		/// Returns a random DateTime within a specified range.
		/// </summary>
		/// <param name="minValue">The lower bound of the random DateTime returned.</param>
		/// <param name="maxValue">The upper bound of the random DateTime returned. maxValue must be greater than or equal to minValue.</param>
		/// <returns></returns>
		public static DateTime Between(this INext<DateTime> next, DateTime minValue, DateTime maxValue)
		{
			Contract.Requires(minValue < maxValue);
			Contract.Requires(next != null);

			var ticks = next.Generator.Next<long>().Between(minValue.Ticks, maxValue.Ticks);
			if(ticks >= maxValue.Ticks)
			{
				ticks = maxValue.Ticks - 1;
			}
			return new DateTime(ticks);
		}


		/// <summary>
		/// Returns a random DateTime within a specified age range.
		/// </summary>
		/// <param name="minAge">The lower bound of the random DateTime returned.</param>
		/// <param name="maxAge">The upper bound of the random DateTime returned. maxAge must be greater than or equal to minAge.</param>
		/// <returns></returns>
		public static DateTime BetweenAge(this INext<DateTime> next, int minAge, int maxAge)
		{
			Contract.Requires(minAge < maxAge);
			Contract.Requires(next != null);

			var minDate = new DateTime(DateTime.Now.Year - minAge - 1, DateTime.Now.Month, DateTime.Now.Day + 1);
			var maxDate = new DateTime(DateTime.Now.Year - maxAge, DateTime.Now.Month, DateTime.Now.Day);
			return next.Between(minDate, maxDate);
		}

		#endregion
	}
}
